package org.lib93.com.libSkin.facctory

import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import org.lib93.com.libSkin.SkinThemeManager
import org.lib93.com.libSkin.SkinViewManager
import java.lang.reflect.Constructor
import java.util.*
import kotlin.reflect.KClass

/***
 * 自定义Factory2来接替系统原有的createView过程
 *
 * 用来收集所有别的view
 * 第一种方案
 * 每个页面加载都会经历以下流程，所以需要替换每一个页面加载时产生的Factory2
 * LoginActivity.setContentView(layout)  ==》  getDelegate().setContentView(view)
 *  ==》AppCompatDelegateImpl.setContentView(View) ==> this.ensureSubDecor()
 *          ==》this.createSubDecor(){L1004   mWindow.setContentView(subDecor);  }
 *            ==> phoneWindow.setContentView()  ==> this. installDecor();
 *              ==> mDecor = generateDecor(-1);//初始化DecorView
 *                ==>  mContentParent = generateLayout(mDecor);
 *                ==>L2664 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
 *                ==>DecorView:final View root = inflater.inflate(layoutResource, null);
 *                  ==> LayoutInflater :return inflate(parser, root, attachToRoot);
 *                      ==>    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
 *                           ==> View view = tryCreateView(parent, name, context, attrs);
 *                              ==>{
 *                                      iew view;
                                        if (mFactory2 != null) {
                                        view = mFactory2.onCreateView(parent, name, context, attrs);
                                        } else if (mFactory != null) {
                                        view = mFactory.onCreateView(name, context, attrs);
                                        } else {
                                        view = null;
                                        }

                                        if (view == null && mPrivateFactory != null) {
                                        view = mPrivateFactory.onCreateView(parent, name, context, attrs);
                                        }
 *
 *                              }
 * 第二种方案 单字节码插桩 在createView中收集view
                         *  try {
                        View view = tryCreateView(parent, name, context, attrs);

                        if (view == null) {
                        final Object lastContext = mConstructorArgs[0];
                        mConstructorArgs[0] = context;
                        try {
                        if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                        } else {
                        view = createView(context, name, null, attrs);
                        }
                        } finally {
                        mConstructorArgs[0] = lastContext;
                        }
                        }
 第三种方案 自定义LayoutInflater 替换原LayoutInflater中的重名资源名
 *
 *
 * SkinFactory2 设置自身为观察者  自身记录了所有的view 一旦接收到更换皮肤的通知 view进行重置
 */
class SkinFactory2 constructor(var activity:Activity):LayoutInflater.Factory2,Observer{
    private val sConstructorMap = HashMap<String, Constructor<out View?>>()//缓存view 的构造方法
    private val mClassPrefixList = arrayOf(
        "android.widget.",
        "android.webkit.",
        "android.app.",
        "android.view."
    )
    private val mConstructorSignature = arrayOf(
        Context::class.java, AttributeSet::class.java
    )
    override fun onCreateView(
        parent: View?,
        name: String,
        context: Context,
        attrs: AttributeSet
    ): View? {
        Log.d("lib093", "add ===="+name)
        var view:View?
        //先判断 标签是 android 相同包的 <Button> 还是三方包的 <xxxx.xxx.x.MyButton>
        if (-1 != name.indexOf('.')) {
            view = createExtendView(name,context,attrs)
            if(null != view) {
                SkinViewManager.getInstance().add(view, attrs)//记录view 和其携带的属性值
            }
            return view
        } else {
            //不包含点就是<Button>这一类 反射创建view看看能不能获取到
           for (it in mClassPrefixList) {
                view = createView(it + name,context,attrs)
                if(null != view) {
                    //解析得到View后 记录view 和view 的属性 用于更换对应次元
//                    <EditText
//                    android:id="@+id/username"
//                    android:layout_width="0dp"
//                    android:layout_height="wrap_content"
//                    android:layout_marginTop="96dp"
//                    android:hint="@string/prompt_email"
//                    android:inputType="textEmailAddress"
//                    android:selectAllOnFocus="true"
//                    app:layout_constraintEnd_toEndOf="parent"
//                    app:layout_constraintStart_toStartOf="parent"
//                    app:layout_constraintTop_toTopOf="parent" />
                    SkinViewManager.getInstance().add(view,attrs)//记录view 和其携带的属性值
                    return view
                }
            }
        }
        return null
    }

    /**
     * 创建扩展自定义扩展View
     */
    private fun createExtendView(name: String, context: Context, attrs: AttributeSet): View? {
        try {
            //这里个能获取不到 比如正确android.widget.Button解析错误android.app.Button 就错了
            //使用classloader通过全类名进行加载
            val clzz = context.classLoader.loadClass(name).asSubclass(View::class.java)
            val constructor1 = clzz.getConstructor(*mConstructorSignature)
            sConstructorMap[name] = constructor1 //sConstructorMap[android.widget.Button] = constructor1
            return constructor1.newInstance(context, attrs)
        }catch (e:Exception){
            return null
        }
        return null
    }

    override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {

        return null
    }

    //反射床架View xml 解析调用的view 两个参数的构造方法 所以自定义view自定义view一定要实现 两个参数的构造方法
//    public MyButton(Context context, AttributeSet attrs) {
//        super(context, attrs);
//    }
    fun createView(name: String, context: Context, attrs: AttributeSet): View? {
        val constructor = sConstructorMap[name]
        if (null == constructor){
            //这种控件第一次遇到
            try {
                //这里个能获取不到 比如正确android.widget.Button解析错误android.app.Button 就错了
                //使用classloader通过全类名进行加载
                val clzz = context.classLoader.loadClass(name).asSubclass(View::class.java)
                val constructor1 = clzz.getConstructor(*mConstructorSignature)
                sConstructorMap[name] = constructor1 //sConstructorMap[android.widget.Button] = constructor1
            }catch (e:Exception){
                return null
            }

        }

        return sConstructorMap[name]?.newInstance(context, attrs)

    }

    override fun update(o: Observable?, arg: Any?) {
        //接到通知进行换肤
        SkinThemeManager.getInstance().updateStatusBarColor(activity)
        SkinViewManager.getInstance().apply()
    }
}